//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/Resources/Font.h"
#include "Graphics/Tools/TextureLineAtlas.h"

namespace Engine
{
namespace Graphics
{


	//
	// Vector Font Manager
	//

	struct VectorFontManager
	{
		friend struct FontLoader;

	// types
	public:
		
		typedef	struct _FT_Face_ *		FT_Face_t;
		typedef struct _TexAtlas *		tex_atlas_id_t;


		//
		// Font Face
		//
		class FontFace : public Referenced
		{
		// variables
		private:
			FT_Face_t		_face;
			MemRFilePtr		_mem;

		// methods
		public:
			FontFace (const MemRFilePtr &mem, FT_Face_t face) : _mem(mem), _face(face) {}
			~FontFace ();

			FT_Face_t	GetFace ()	const	{ return _face; }
		};


		SHARED_POINTER( FontFace );
		

	private:

		//
		// Allocated Font Textures
		//

		struct AllocFontTex
		{
			Ptr< VectorFont >	font;
			uint				height;
			bool				isFull;
			///
			AllocFontTex () : height(0), isFull(false) {}
			AllocFontTex (Ptr<VectorFont> font, uint height) : font(font), height(height), isFull(false) {}

			bool operator == (const AllocFontTex &right) const
			{
				return font == right.font and height == right.height and isFull == right.isFull;
			}

			bool operator >  (const AllocFontTex &right) const
			{
				return	  font != right.font ? font.ptr() > right.font.ptr() :
						( height != right.height ? height > right.height :
						  isFull > right.isFull );
			}

			bool operator <  (const AllocFontTex &right) const
			{
				return	  font != right.font ? font.ptr() < right.font.ptr() :
						( height != right.height ? height < right.height :
						  isFull < right.isFull );
			}
		};
		

		struct AnyFontTex
		{
			Ptr< VectorFont >	font;
			uint				height;
			///
			AnyFontTex () : height(0) {}
			AnyFontTex (Ptr<VectorFont> &font, uint height) : font(font), height(height) {}

			bool operator == (const AllocFontTex &right) const
			{
				return font == right.font and height == right.height;
			}

			bool operator >  (const AllocFontTex &right) const
			{
				return	  font != right.font ? font.ptr() > right.font.ptr() :
						( height != right.height ? height > right.height :
						  false > right.isFull );
			}

			bool operator <  (const AllocFontTex &right) const
			{
				return	  font != right.font ? font.ptr() < right.font.ptr() :
						( height != right.height ? height < right.height :
						  false < right.isFull );
			}
		};


		struct FindAnyFont
		{
			Ptr< VectorFont >	font;
			///
			FindAnyFont (Ptr<VectorFont> &fnt) : font(fnt) {}
			
			bool operator == (const AllocFontTex &right) const
			{
				return font == right.font;
			}

			bool operator >  (const AllocFontTex &right) const
			{
				return font.ptr() > right.font.ptr();
			}

			bool operator <  (const AllocFontTex &right) const
			{
				return font.ptr() < right.font.ptr();
			}
		};


		//
		// Texture Atlas
		//
		class TexAtlas : public Referenced
		{
		public:
			TextureLineAtlas	atlas;
			Texture				tex;
			///
			TexAtlas (const EngineSubSystemsRef ess) : atlas(), tex(ess) {}
		};
		
		SHARED_POINTER( TexAtlas );
		
		typedef TextureLineAtlas::SubTextureId	atlas_tex_id_t;
		typedef array< TexAtlasPtr >			tex_atlas_array_t;


		//
		// Texture Line
		//

		struct TexLine
		{
			TexAtlasPtr		atlas;
			atlas_tex_id_t	line;
			///
			TexLine () {}
			TexLine (const TexAtlasPtr &atlas, atlas_tex_id_t id = atlas_tex_id_t()) : atlas(atlas), line(id) {}
		};

		
		typedef map< string, FontFacePtr >		fontface_map_t;
		typedef map< AllocFontTex, TexLine >	font_lines_map_t;

		enum {
			_ATLAS_WIDTH	= 1024,
			_ATLAS_HEIGHT	= 1024,
			_MARGIN_X		= 1,
			_MARGIN_Y		= 1,
			_MARGIN			= (_MARGIN_X + _MARGIN_Y) * 2
		};


	// variables
	private:
		tex_atlas_array_t	_atlases;
		fontface_map_t		_faces;
		font_lines_map_t	_lines;
		uint const			_lineWidth;


	// methods
	public:
		VectorFontManager ();


	protected:
		bool _CreateLevel (Ptr< VectorFont > font, uint height);
		bool _DestroyLevel (Ptr< VectorFont > font, uint height);

		bool _DestroyFontLevels (Ptr< VectorFont > font);

		void _ReleaseEmptyAtlases ();

		bool _LoadGlyph (Ptr< VectorFont > font, uint8 symbol, uint height, OUT IFont::Glyph &glyph);

		void _Clear ();

		FontFacePtr	_Find (uni_c_string filename) const;
	};

	

/*
=================================================
	constructor
=================================================
*/
	inline VectorFontManager::VectorFontManager () : _lineWidth(_ATLAS_WIDTH)
	{
	}

/*
=================================================
	_Find
=================================================
*/
	inline VectorFontManager::FontFacePtr  VectorFontManager::_Find (uni_c_string filename) const
	{
		fontface_map_t::iterator	res;

		if ( _faces.Find( filename, res ) )
		{
			return res->second;
		}
		return null;
	}
	
/*
=================================================
	_Clear
=================================================
*/
	inline void VectorFontManager::_Clear ()
	{
		_lines.Clear();
		_atlases.Clear();
		_faces.Clear();
	}
	
/*
=================================================
	_DestroyLevel
=================================================
*/
	inline bool VectorFontManager::_DestroyLevel (Ptr< VectorFont > font, uint height)
	{
		font_lines_map_t::iterator	res;

		while ( _lines.Find( AnyFontTex( font, height ), res ) )
		{
			res->second.atlas->atlas.DeallocLine( res->second.line );
			_lines.EraseFromIter( res );
		}

		_ReleaseEmptyAtlases();
		return true;
	}
	
/*
=================================================
	_DestroyFontLevels
=================================================
*/
	inline bool VectorFontManager::_DestroyFontLevels (Ptr< VectorFont > font)
	{
		font_lines_map_t::iterator	res;

		while ( _lines.Find( FindAnyFont( font ), res ) )
		{
			res->second.atlas->atlas.DeallocLine( res->second.line );
			_lines.EraseFromIter( res );
		}

		_ReleaseEmptyAtlases();
		return true;
	}
	
/*
=================================================
	_ReleaseEmptyAtlases
=================================================
*/
	inline void VectorFontManager::_ReleaseEmptyAtlases ()
	{
		if ( _lines.Empty() )
		{
			_atlases.Clear();
			return;
		}

		FOR( i, _atlases )
		{
			if ( _atlases[i].GetRefCount() == 1 and _atlases[i]->atlas.Empty() )
			{
				_atlases.Erase( i );
				--i;
			}
		}
	}


}	// Graphics
}	// Engine